package scalab.core.component

import scalab.utils.Reporter
import scalab.caches.NoCache
import scala.collection.immutable.{Set, HashSet}
import scalab.core.gates.{Input, Output}
import scalab.core.dependencies.Dependency
//extends Task...set local NoCache
//(in ./.scalab/targets/) 
//sealed trait...
sealed trait Target extends Task{
	val targetDirectory: String
	val targets = mandatoryIn

	setCache(NoCache())

	//implement later...
	/*
	private[core] def upToDate(fresh: List[Buildable]): Pair[Boolean, List[Buildable]] = 
	if(fresh contains this)
		(true, fresh)
	else{
		var newFresh : List[Buildable] = fresh
		(targets forall {t =>
			//check for fresh here? not that useful...
			val (toDate, moreFresh) = t upToDate fresh
			if(toDate)
				newFresh = newFresh union moreFresh
			toDate
		}, newFresh)
	}*/

	//def locallyUpToDate

	/*private[core] def upToDate(
		products: Set[Resource],
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		fresh: Set[ExecutableComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[ExecutableComponent]
		) =
			utils.AlgorithmFactory.createUpToDate(targets)(
				products, 
				visited, 
				cycles, 
				fresh
			)*/

	def upToDate: Boolean = {
		//info("update")
		val latest = lastProducts
		!latest.isEmpty &&
		upToDate(
			latest.get, 
			HashSet.empty[Component], 
			HashSet.empty[(Output, Input)], 
			HashSet.empty[ExecutableComponent]
		)._1
	}

	def update: Boolean = update(
			HashSet.empty[Component], 
			HashSet.empty[(Output, Input)], 
			HashSet.empty[ExecutableComponent]
		)._1

	private[core] def lastProducts: Option[Set[Resource]] = {
		HashSet.empty[Resource]
		val (res, opt) = getCache.getRunProducts(this, Nil): (Boolean, Option[Set[Dependency]])
		if(res && !opt.isEmpty)Some(opt.get map {d => d.getResource})
		else None
	}

	//special execute method which copies and stores list of products in ./scalab/targets/target.id

	
	//private[core] def locallyUpToDate(products: Set[Resource]): Boolean

	def execute(productDir: String): Boolean = {
		import scalab.utils.{FileUtils, Path}
		import java.io.File
		//verify when product has been deleted...
		//do{
		targets.providers forall {p =>
			val deps = p.productDependencies
			//info("target.productDirectories:" + p.productDirs.mkString(""," ",""))
			//info("target.out.dependencies: " + deps.mkString("\n","\n",""))
			p.productDirs forall {dir =>
				val folder = Resource(dir)
				(deps filter {d => d.canonicalPath.startsWith(folder.canonicalPath)}) forall {d =>
					val entry = new File(targetDirectory + Path.removePrefix(d, folder.canonicalPath))
					FileUtils.mkDirs(entry.getParent)//check for succes?
					//warning("copy from " + d + " to " + entry)
					FileUtils.copyFile(d, entry)
				}
			}
		} 
	}

  /*private[core] def buildTarget: Boolean = {
		import scalab.utils.{FileUtils, Path}
		import java.io.File
		//verify when product has been deleted...
		//do{
		if(!update(HashSet.empty[Component], HashSet.empty[(Output, Input)], HashSet.empty[ExecutableComponent])._1)
			return false

		targets forall {target =>
			val deps = target.productDependencies
			info("target.productDirectories:" + target.productDirs.mkString(""," ",""))
			info("target.out.dependencies: " + deps.mkString("\n","\n",""))
			target.productDirs forall {dir =>
				val folder = Resource(dir)
				(deps filter {d => d.canonicalPath.startsWith(folder.canonicalPath)}) forall {d =>
					val entry = new File(targetDirectory + Path.removePrefix(d, folder.canonicalPath))
					FileUtils.mkDirs(entry.getParent)//check for succes?
					warning("copy from " + d + " to " + entry)
					FileUtils.copyFile(d, entry)
				}
			}
		} */
/*		target.update && (sourceDirs forall {s =>
			FileUtils.copyDirectory(s, targetDirectory)
		})*/
		//}while(BuildEntry.modified)
//	}

	//private[core] def validate: Boolean = targets forall {t => t.validate}//optimize like for upToDate?
	/*private[core] def validate(validated: Set[Buildable]): Pair[Boolean, Set[Buildable]] = {
		utils.AlgorithmFactory.createValidate(targets)(validated)
	}*/

	/*private[core] def validate(
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		validated: Set[CustomComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[CustomComponent]
		) = utils.AlgorithmFactory.createValidate(targets)(visited, cycles, validated)*/

	//def updateCompId: Unit = targets.providers foreach {p => p.updateId}//idem

	private[core] def validate: Boolean = validate(HashSet.empty[Component], HashSet.empty[(Output, Input)], HashSet.empty[SinkComponent])._1

	/*private[core] def update(
		visited: Set[Component], 
		cycles: Set[(Output, Input)], 
		fresh: Set[ExecutableComponent]): (
			Boolean, 
			Set[Component], 
			Set[(Output, Input)], 
			Set[ExecutableComponent]
		) = utils.AlgorithmFactory.createUpdate(targets)(visited, cycles, fresh)*/

	/*private[core] def update(fresh: Set[ExecutableComponent]): Pair[Boolean, Set[ExecutableComponent]] = 
		(targets forall {t => t.update(fresh)._1}, fresh)*/
		//utils.AlgorithmFactory.createUpdate(this, targets)(fresh)

	def instanceId: String = ""

	Target.allTargets = Target.allTargets + this
}

object Target{

	private[core] var allTargets: Set[Target] = HashSet.empty[Target]
	def getAll: Set[Target] = allTargets

	import scalab.utils.Path

	def apply(destinationDir: String, components: Iterable[SinkComponent]) = {
		val target = new Target{
			override val name = "scalab"
			override val targetDirectory = Path(destinationDir)
		}
		target << components
		target
	}
	
	def apply(destinationDir: String, components: SinkComponent*): Target = 
		apply(destinationDir, components)
	
}
